package com.example.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Random;

// 1. @Component：将切面类托管到Spring IOC容器（必须，否则Spring无法扫描到切面）
@Component
// 2. @Aspect：标记此类为AOP切面类（包含增强逻辑和切入点）
@Aspect
public class OrderServiceAspect {

    // ==================== 第一步：定义切入点（统一匹配目标方法） ====================
    // @Pointcut：定义切入点，表达式匹配com.example.service包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void orderServicePointcut() {} // 切入点方法：无逻辑，仅作为标识


    // ==================== 需求1：前置增强（所有方法调用前输出提示） ====================
    // @Before：前置通知，在切入点方法执行前执行
    @Before("orderServicePointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        // JoinPoint：获取目标方法信息（如方法名、参数等，可选）
        String methodName = joinPoint.getSignature().getName(); // 获取当前执行的方法名
        System.out.println("\n【前置增强】hello, 我是一个前置增强 | 目标方法：" + methodName);
    }


    // ==================== 需求2：后置增强（add/delete方法执行后输出提示） ====================
    // @AfterReturning：后置通知（方法正常执行完后触发），切入点仅匹配add和delete方法
    @AfterReturning("execution(* com.example.service.*.add(..)) || execution(* com.example.service.*.delete(..))")
    public void afterReturningAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("【后置增强】bye, 我是一个后置增强 | 目标方法：" + methodName);
    }


    // ==================== 需求3：获取方法返回值并输出（所有方法） ====================
    // @AfterReturning：通过returning属性指定接收返回值的参数名（必须与方法参数名一致）
    @AfterReturning(
            pointcut = "orderServicePointcut()", // 切入点
            returning = "result" // 接收返回值的参数名，类型为Object（兼容所有返回值类型）
    )
    public void afterReturningWithResultAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        // 注意：void方法（add/delete）的返回值为null，需特殊处理
        if (result == null) {
            System.out.println("【返回值增强】目标方法：" + methodName + " | 返回值：无（void方法）");
        } else {
            System.out.println("【返回值增强】目标方法：" + methodName + " | 返回值：" + result);
        }
    }


    // ==================== 需求4：输出方法执行时间（所有方法） ====================
    // @Around：环绕通知（最灵活，可在方法执行前后、异常时增强），需返回方法执行结果
    @Around("orderServicePointcut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // ProceedingJoinPoint：是JoinPoint的子类，支持调用目标方法（必须）
        String methodName = proceedingJoinPoint.getSignature().getName();

        // 1. 方法执行前：记录开始时间
        long startTime = System.currentTimeMillis(); // 毫秒级时间戳

        // 2. 调用目标方法（核心：必须执行proceed()，否则目标方法不会执行）
        Object result = proceedingJoinPoint.proceed(); // 接收目标方法返回值

        // 3. 方法执行后：计算执行时间
        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime; // 执行时间（毫秒）

        // 输出执行时间
        System.out.println("【执行时间增强】目标方法：" + methodName + " | 执行时间：" + costTime + "毫秒");

        // 返回目标方法结果（环绕通知必须返回，否则调用者无法获取返回值）
        return result;
    }


    // ==================== 需求5：add/delete方法前置权限校验（随机数模拟） ====================
    // 用@Around实现：因为需要“无权限时终止方法执行”，前置通知（@Before）无法终止
    @Around("execution(* com.example.service.*.add(..)) || execution(* com.example.service.*.delete(..))")
    public Object authCheckAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String methodName = proceedingJoinPoint.getSignature().getName();

        // 1. 模拟权限校验：生成0或1的随机数（0=有权限，1=无权限）
        Random random = new Random();
        int authFlag = random.nextInt(2); // 生成[0,1]之间的整数

        // 2. 判断权限
        if (authFlag == 0) {
            System.out.println("【权限校验增强】目标方法：" + methodName + " | 权限校验通过（authFlag=0），允许执行");
            // 有权限：执行目标方法并返回结果
            return proceedingJoinPoint.proceed();
        } else {
            System.out.println("【权限校验增强】目标方法：" + methodName + " | 权限校验失败（authFlag=1），终止执行");
            // 无权限：不执行目标方法，返回null（void方法返回null不影响）
            return null;
        }
    }
}